package hippo

import (
	"encoding/json"
	"fmt"
	"images-bed/common"
	"images-bed/config"
	"images-bed/papers"
	"images-bed/util"
	"io"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Hippo 服务器
type Hippo struct {
	Listener *net.Listener
	wait     sync.WaitGroup
}

// 发送
func (hippo *Hippo) send(w http.ResponseWriter, bytes []byte) {
	_, err := w.Write(bytes)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
}

// 发送 JSON
func (hippo *Hippo) sendJSON(w http.ResponseWriter, objs ...interface{}) {
	if len(objs) > 0 {
		bytes, err := json.Marshal(objs[0])
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
		} else {
			hippo.send(w, bytes)
		}
	}
}

// 发送文件
func (hippo *Hippo) sendFile(w http.ResponseWriter, r *http.Request, paper *papers.Paper) {
	reader := papers.GetPapers().OpenPaperReader(*paper)
	stat, err := reader.File.Stat()
	if err != nil {
		if os.IsNotExist(err) {
			hippo.sendJSON(w, common.Fail(4004, "文件不存在"))
		} else {
			hippo.sendJSON(w, common.Fail(4005, "系统错误"))
		}
	} else {
		if !stat.IsDir() {
			if r.URL.Query().Get("download") == "1" {
				w.Header().Add("Content-Disposition", "attachment; filename="+
					paper.Name+"."+paper.Suffix)
			}
			w.Header().Add("Accept-Ranges", "bytes")
			w.Header().Set("Content-Length", strconv.FormatInt(stat.Size(), 10))
			_, _ = io.Copy(w, reader.Reader)
			papers.GetPapers().CloseReaderPaper(reader)
		} else {
			hippo.sendJSON(w, common.Fail(406, "文件夹不能下载"))
		}
	}
}

type UploadResult struct {
	Name     string `json:"name"`
	URL      string `json:"url"`
	Suffix   string `json:"suffix"`
	FileName string `json:"filename"`
}

func (hippo *Hippo) acceptFile(w http.ResponseWriter, r *http.Request) {
	scene := r.FormValue("scene")
	now := time.Now()
	path := papers.Path{
		Scene: scene,
		Year:  now.Year(),
		Month: int(now.Month()),
		Day:   now.Day(),
	}

	err := r.ParseMultipartForm(32 << 20)
	if err != nil {
		hippo.sendJSON(w, common.Fail(5000, "服务错误"))
		return
	}

	files := r.MultipartForm.File[config.GetConfig().UploadName]
	size := len(files)
	var paperList []UploadResult
	for i := 0; i < size; i++ {
		file := files[i]
		filename := file.Filename
		point := strings.LastIndex(filename, ".")
		paperId := util.UUID()
		paper := papers.Paper{
			Path:    path,
			PaperId: paperId,
			Name:    filename[0:point],
			Suffix:  strings.ToLower(filename[point+1:]),
		}
		writer := papers.GetPapers().OpenPaperWriter(paper)
		reader, err := file.Open()
		if err != nil {
			hippo.sendJSON(w, common.Fail(5000, "上传错误"))
			return
		}
		_, _ = io.Copy(writer.Writer, reader)
		papers.GetPapers().CloseWriterPaper(writer)
		paperList = append(paperList, UploadResult{
			paper.Name,
			hippo.PaperAddr(paper),
			paper.Suffix,
			paper.Name + "." + paper.Suffix})
	}

	hippo.sendJSON(w, common.SuccessWithData("ok", paperList))
	return
}

func (hippo *Hippo) BaseAddr() string {
	return fmt.Sprintf("http://%s:%d/", config.GetConfig().Host, config.GetConfig().Port)
}

func (hippo *Hippo) PaperAddr(paper papers.Paper) string {

	host := config.GetConfig().Host
	if host == "" {
		if config.GetConfig().Protocol == "tcp4" {
			host = "localhost"
		} else {
			host = "[::]"
		}
	}

	return fmt.Sprintf("http://%s:%d%s/%s/%d/%d/%d/%s",
		host,
		config.GetConfig().Port,
		config.GetConfig().StaticPrefix,
		paper.Path.Scene,
		paper.Path.Year,
		paper.Path.Month,
		paper.Path.Day,
		paper.PaperId+"-"+paper.Name+"."+paper.Suffix,
	)
}

func (hippo *Hippo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	StaticPrefix := config.GetConfig().StaticPrefix
	UploadPrefix := config.GetConfig().UploadPrefix
	uri := r.URL.Path
	if strings.Index(uri, StaticPrefix) == 0 {
		// 说明是静态文件
		original := uri[len(StaticPrefix)+1:]
		splits := strings.Split(original, "/")
		if len(splits) == 5 {
			var err error
			year, err := strconv.Atoi(splits[1])
			month, err := strconv.Atoi(splits[2])
			day, err := strconv.Atoi(splits[3])
			if err == nil {
				filename := splits[4]
				index := strings.Index(filename, "-")
				point := strings.LastIndex(filename, ".")
				paperId := filename[0:index]
				name := filename[index+1 : point]
				suffix := filename[point+1:]
				paper := papers.Paper{
					Path: papers.Path{
						Scene: splits[0],
						Year:  year,
						Month: month,
						Day:   day,
					},
					PaperId: paperId,
					Name:    name,
					Suffix:  strings.ToLower(suffix),
				}
				hippo.sendFile(w, r, &paper)
			} else {
				hippo.sendJSON(w, common.Fail(4008, "路径解析错误"))
			}
		} else {
			hippo.sendJSON(w, common.Fail(407, "下载地址 场景/年/月/日/文件ID-文件名.文件后缀"))
		}
	} else if strings.Index(uri, UploadPrefix) == 0 && r.Method == "POST" {
		// 上传文件
		hippo.acceptFile(w, r)
	} else {
		hippo.send(w, []byte(fmt.Sprintf(config.GetDefaultHtml(),
			UploadPrefix, config.GetConfig().UploadName)))
	}
}

func (hippo *Hippo) start() {
	err := http.Serve(*hippo.Listener, hippo)
	if err != nil {
		panic("server start fail")
	}
}

// 启动服务器
func (hippo *Hippo) Start() {
	go hippo.start()
	hippo.wait.Add(1)
}

// 初始化服务器
func NewBoot(host string, port int, protocol string) (*Hippo, error) {
	listener, err := net.Listen(protocol,
		fmt.Sprintf("%s:%d", host, port))
	if err != nil {
		return &Hippo{}, err
	}
	hippo := Hippo{
		Listener: &listener,
		wait:     sync.WaitGroup{},
	}
	return &hippo, nil
}

// 关闭服务器
func (hippo *Hippo) Stop() {
	(*hippo.Listener).Close()
	hippo.wait.Done()
}

// 开始等待监听
func (hippo *Hippo) Accept() {
	hippo.wait.Wait()
}
